/**
 * Copyright 2014 opencxa.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cxa.rpc.provider;

import java.util.Arrays;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.cxa.rpc.messages.NotificationRpcMessage;
import org.cxa.rpc.messages.RequestRpcMessage;
import org.cxa.rpc.messages.ResponseRpcMessage;
import org.cxa.rpc.messages.RpcMessage;
import org.cxa.rpc.provider.RpcException.RpcError;
import org.cxa.rpc.session.SessionManager;
import org.cxa.rpc.session.SessionManager.SessionListener;
import org.cxa.rpc.transportChannel.TransportChannel.DisconnectReason;


/**
 * @author Christopher Armenio
 */
public abstract class ComponentTreeProvider extends Component implements SessionListener
{	
	private final SessionManager sessMan;
	private final Logger logger = LogManager.getLogger(this.toString());
	
	
	public ComponentTreeProvider(SessionManager sessManIn)
	{
		super("_ctp");						// doesn't really matter since we don't compare for CTP
		
		this.sessMan = sessManIn;
		if( this.sessMan != null ) this.sessMan.addSessionListener(this);
	}
	
	
	public void sendMessage(RpcMessage msgIn)
	{
		if( sessMan != null ) this.sessMan.sendMessage(msgIn);
	}
	
	
	protected SessionManager getSessionManager()
	{
		return this.sessMan;
	}
	
	
	@Override
	public void onSessionOpen() {}
	
	
	@Override
	public void onSessionClose(DisconnectReason reasonIn) {}
	
	
	@Override
	public void onRequestReceived(RequestRpcMessage msgIn)
	{
		String[] pathComponents = RpcMessage.parsePathComponents(msgIn.getDestination());
		if( pathComponents == null ) 
		{
			this.sendMessage(new ResponseRpcMessage(msgIn, RpcException.exceptionFromError(RpcError.INVALID_DEST_PATH)));
			return;
		}
		Component currComponent = ComponentTreeProvider.this;
		for( int i = 0; i < pathComponents.length; i++ )
		{
			String currPc = pathComponents[i];
			
			Component nextComponent = currComponent.getSubComponent_byName(currPc);
			CatchAllHandler cah = null;
			if( nextComponent != null ) currComponent = nextComponent;
			else if( (cah = currComponent.getCatchAllHandler()) != null )
			{
				cah.onRequestReceived(msgIn, Arrays.copyOfRange(pathComponents, i, pathComponents.length));
				return;
			}
			else
			{
				this.sendMessage(new ResponseRpcMessage(msgIn, RpcError.UNKNOWN_COMPONENT));
				return;
			}
		}
		
		// if we made it here, we found the correct component...try to call the method
		ResponseRpcMessage respMsg = null;
		try
		{
			Object returnValue = currComponent.notify_onRequestReceived(msgIn);
			// if we made it here, the command was successful!
			respMsg = new ResponseRpcMessage(msgIn, returnValue);
		}
		catch(RpcException e)
		{
			respMsg = new ResponseRpcMessage(msgIn, e);
		}
		catch(Exception e)
		{
			respMsg = new ResponseRpcMessage(msgIn, RpcException.exceptionFromError(RpcError.ERROR_COMMAND_HANDLER, e.getMessage()));
		}
		if( respMsg != null ) this.sendMessage(respMsg);
	}
	
	
	@Override
	public void onNotificationReceived(NotificationRpcMessage msgIn)
	{
	}
	
	
	@Override
	public void onUnmatchedResponseReceived(ResponseRpcMessage msgIn)
	{	
		// if we made it here, we have a destination path...parse it and use it
		String[] pathComponents = RpcMessage.parsePathComponents(msgIn.getDestination());
		if( pathComponents == null ) 
		{
			// can't have a response (error) to a response...just drop it
			return;
		}
		Component currComponent = ComponentTreeProvider.this;
		for( int i = 0; i < pathComponents.length; i++ )
		{
			String currPc = pathComponents[i];
			
			Component nextComponent = currComponent.getSubComponent_byName(currPc);
			CatchAllHandler cah = null;
			if( nextComponent != null ) currComponent = nextComponent;
			else if( (nextComponent == null) && (cah = currComponent.getCatchAllHandler()) != null )
			{
				cah.onResponseReceived(msgIn, Arrays.copyOfRange(pathComponents, i, pathComponents.length));
				return;
			}
		}
	}
	
	
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return String.format("%s.%s", this.getClass().getSimpleName(), System.identityHashCode(this));
	}
}
